//
// Created by DELL on 2022/4/21.
//
#pragma once
#include "../Utils.h"
#include "../VulkanDefine.h"
#include "DescriptorSet.h"

#include <SPIRV/GlslangToSpv.h>
#include <SPIRV/SpvTools.h>

BEGIN_NS_VULKAN

struct ShaderInfo
{
    VkShaderStageFlagBits stage{VK_SHADER_STAGE_ALL};
    std::string source;
    bool isPath = true;

    ShaderInfo() = default;

    ShaderInfo(VkShaderStageFlagBits stage, std::string source, bool isPath = true)
        : stage(stage),  source(std::move(source)) {}
};

class Shader
{
public:
    ~Shader() = default;
    Shader(const Shader&) = delete;
    Shader& operator=(const Shader&) = delete;

    static ShaderRef create(const std::vector<ShaderInfo>& si);
    static ShaderRef create(const std::string& vertSrc, const std::string& fragSrc);
    static ShaderRef createByPath(const std::string& vertPath, const std::string& fragPath);

    size_t getStageCount() const { return this->stages.size(); }
    VkShaderStageFlagBits getStage(int i) const { return stages[i]; }
    const std::vector<unsigned int>& getSpirv(int i) const { return spirvCodes[i]; }

    const DescriptorSetLayout& getDescriptorSetLayout() const { return this->descriptorSetLayout; }

    void dumpSpirv(const std::string& fileName);

private:
    Shader() = default;
    void initDescriptorSetLayout(glslang::TProgram* program);

    static EShLanguage stage2ShLanguage(VkShaderStageFlagBits stage);
    static void populateBuiltInResource(TBuiltInResource& resources);
    static std::shared_ptr<glslang::TShader> createShader(const std::string& src, EShLanguage language);
    static std::shared_ptr<glslang::TProgram> createProgram(const std::vector<std::shared_ptr<glslang::TShader>>& shaders);
    static UniformType glslangType2UniformType(const glslang::TType* type);
    static VkShaderStageFlagBits glslangStageBit2Vulkan(EShLanguageMask stage);
    static VkShaderStageFlags glslangStages2Vulkan(EShLanguageMask stages);

    std::vector<VkShaderStageFlagBits> stages;
    std::vector<std::vector<uint32_t>> spirvCodes;
    DescriptorSetLayout descriptorSetLayout;

    static bool isInitGlslang;
    static CustomEventListenerRef systemExitEvent;
};

END_NS_VULKAN
